package com.bytedance.microapp;

import java.util.Base64;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.security.Signature;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.KeyFactory;
import java.nio.charset.StandardCharsets;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * 抖音签名
 */
public class DYSign {

    /**
     * 获取抖音授权
     * @param privateKeyStr 私钥
     * @param data 数据
     * @param appId 应用ID
     * @param nonceStr 随机字符串
     * @param timestamp 时间戳
     * @param keyVersion 密钥版本
     * @return 抖音授权
     */
    public static String getByteAuthorization(String privateKeyStr, String data, String appId, String nonceStr, long timestamp, String keyVersion) {
        return getByteAuthorization(privateKeyStr, "/requestOrder", data, appId, nonceStr, timestamp, keyVersion);
    }

    /**
     * 获取抖音授权
     * @param privateKeyStr 私钥
     * @param uri 请求地址
     * @param data 数据
     * @param appId 应用ID
     * @param nonceStr 随机字符串
     * @param timestamp 时间戳
     * @param keyVersion 密钥版本
     * @return 抖音授权
     */
    public static String getByteAuthorization(String privateKeyStr, String uri, String data, String appId, String nonceStr, long timestamp, String keyVersion) {
        return getByteAuthorization(privateKeyStr, "POST", uri, data, appId, nonceStr, timestamp, keyVersion);
    }

    /**
     * 获取抖音授权
     * @param privateKeyStr 私钥
     * @param method 请求方法
     * @param uri 请求地址
     * @param data 数据
     * @param appId 应用ID
     * @param nonceStr 随机字符串
     * @param timestamp 时间戳
     * @param keyVersion 密钥版本
     * @return 抖音授权
     */
    public static String getByteAuthorization(String privateKeyStr, String method, String uri, String data, String appId, String nonceStr, long timestamp, String keyVersion) {
        String byteAuthorization = "";
        try {
            // 生成签名
            String signature = getSignature(privateKeyStr, method, uri, timestamp, nonceStr, data);
            // 构造byteAuthorization
            StringBuilder sb = new StringBuilder();
            sb.append("SHA256-RSA2048 ")
                .append("appid=").append(appId).append(",")
                .append("nonce_str=").append(nonceStr).append(",")
                .append("timestamp=").append(timestamp).append(",")
                .append("key_version=").append(keyVersion).append(",")
                .append("signature=").append(signature);
            byteAuthorization = sb.toString();
        } catch (Exception ex) {
            Logger.getLogger(DYSign.class.getName()).log(Level.SEVERE, "getByteAuthorization", ex);
            return "";
        }
        return byteAuthorization;
    }

    /**
     * 获取签名
     * @param privateKeyStr 私钥
     * @param method 请求方法
     * @param uri 请求地址
     * @param timestamp 时间戳
     * @param nonce 随机字符串
     * @param data 数据
     * @return 签名
     */
    public static String getSignature(String privateKeyStr, String method, String uri, long timestamp, String nonce, String data) throws Exception {
        String rawStr = method + "\n" +
                uri + "\n" +
                timestamp + "\n" +
                nonce + "\n" +
                data + "\n";
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(string2PrivateKey(privateKeyStr));
        sign.update(rawStr.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(sign.sign());
    }

    /**
     * 将私钥字符串转换为私钥
     * @param privateKeyStr 私钥字符串
     * @return 私钥
     */
    public static PrivateKey string2PrivateKey(String privateKeyStr) {
        PrivateKey prvKey = null;
        try {
            byte[] privateBytes = Base64.getDecoder().decode(privateKeyStr);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateBytes);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            prvKey = keyFactory.generatePrivate(keySpec);
        } catch (Exception ex) {
            Logger.getLogger(DYSign.class.getName()).log(Level.SEVERE, "string2PrivateKey", ex);
        }
        return prvKey;
    }

    /**
     * 验证签名
     * @param httpBody 请求体
     * @param publicKey 公钥
     * @param signStr 签名
     * @param timestamp 时间戳
     * @param nonce 随机字符串
     * @return 是否验证通过
     */
    public static boolean verify(String httpBody, String publicKey, String signStr, String timestamp, String nonce) throws Exception {
        StringBuffer buffer = new StringBuffer();
        buffer.append(timestamp).append("\n");
        buffer.append(nonce).append("\n");
        buffer.append(httpBody).append("\n");
        String message = buffer.toString();
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initVerify(string2PublicKey(publicKey)); // 注意验签时publicKey使用平台公钥而非应用公钥
        sign.update(message.getBytes(StandardCharsets.UTF_8));
        return sign.verify(Base64.getDecoder().decode(signStr.getBytes(StandardCharsets.UTF_8)));
    }

    /**
     * 将公钥字符串转换为公钥
     * @param publicKey 公钥字符串
     * @return 公钥
     */
    public static PublicKey string2PublicKey(String publicKey) throws Exception {
        byte[] decoded = org.apache.commons.codec.binary.Base64.decodeBase64(publicKey);
        return KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
    }
}
